package core;

import core.exception.IllegalAddException;
import core.reports.IReportVisitor;
import core.reports.TraversingType;

/**This class is a Singleton that stores all the StorageUnits in the
 * system. It extends Container because it handles child StorageUnits
 * in the same way StorageUnits and ProductGroups handle child ProductGroups.
 * It also allows the entire tree to be traversed uniformly starting from this
 * node. 
 * 
 * @author Natasha
 *
 */

//Container extends ModelObject, so no need to extend ModelObject here
public class RootStorageUnit extends Container {

	private static final long serialVersionUID = 1583658777654375436L;
	private static RootStorageUnit _sInstance;
	
	/**Return the one and only RootStorageUnit.
	 * 
	 * @return The one and only RootStorageUnit.
	 */
	public static RootStorageUnit instance(){
		// This method has no preconditions
		if (_sInstance == null)
			_sInstance = new RootStorageUnit();
		return _sInstance;
	}
	
	/**Set the static instance of RootStorageUnit to a new instance.
	 * This allows the RootStorageUnit to be loaded.
	 * 
	 * @param newInstance The new RootStorageUnit instance.
	 */
	protected static void setInstance(RootStorageUnit newInstance){
		// This method has no preconditions.
		
		// Note: this method allows the newInstance to be null. If
		// the new instance is null, a new RootStorageUnit will be
		// created on the next call to instance(). This allows 
		// test code to create a new RootStorageUnit for each test.
		_sInstance = newInstance;
	}
	
	/**Add a StorageUnit to the system.
	 * 
	 * @param name The name of the new StorageUnit. The name must be
	 * nonempty and unique.
	 * @throws IllegalAddException
	 * @return The new StorageUnit added.
	 */
	@Override
	protected Container addContainer(String name) throws IllegalAddException {
		// Check preconditions
		if (!canAddContainer(name))
			throw new IllegalAddException("Can't add container");
		
		// Create the new Storage Unit
		StorageUnit su = new StorageUnit(name);
		
		// add it to the map
		this._children.put(name, su);
		
		// set it's internal pointer to point here
		su._parent = this;
		su._storageUnit = su;
		su._storageUnit.addDescendant(su);
		
		// Return it for the user.
		return su;
		
	}
	
	/**The internal constructor. Access the RootStorageUnit through
	 * the instance() method.
	 */
	private RootStorageUnit() {
		// There are no preconditions. This method always succeeds and has 
		// no variables to initialize.
		super("RootStorageUnit");
		setID(0);
		
	}

	@Override
	public void accept(IReportVisitor v) {
		TraversingType t = v.getType();
		if (t == TraversingType.PREORDER) {
			v.visitRoot(this);
			for(Container c : _children.values()) {
				c.accept(v);
			}
		}
		else {
			//POSTORDER
			for(Container c : _children.values()) {
				c.accept(v);
			}
			v.visitRoot(this);
		}
		
	}


}
